home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Graphics Programming (2nd Edition) / Visual Basic Graphics Programming 2nd Edition.iso / Src / Ch17 / Sphere.cls < prev    next >
Text File  |  1999-07-06  |  18KB  |  581 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "RaySphere"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = False
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16. ' A sphere object.
  17.  
  18. Implements RayTraceable
  19.  
  20. ' Geometry.
  21. Private Radius As Single
  22. Private Center As Point3D
  23.  
  24. Private Const NUM_THETA = 10
  25. Private Const NUM_PHI = 10
  26. Private WireFrame(1 To NUM_THETA, 1 To NUM_PHI) As Point3D
  27.  
  28. ' Ambient light parameters.
  29. Private AmbientKr As Single
  30. Private AmbientKg As Single
  31. Private AmbientKb As Single
  32.  
  33. ' Diffuse light parameters.
  34. Private DiffuseKr As Single
  35. Private DiffuseKg As Single
  36. Private DiffuseKb As Single
  37.  
  38. ' Specular reflection parameters.
  39. Private SpecularN As Single
  40. Private SpecularK As Single
  41.  
  42. ' Reflected light parameters.
  43. Private ReflectedKr As Single
  44. Private ReflectedKg As Single
  45. Private ReflectedKb As Single
  46.  
  47. ' Refracted light parameters.
  48. Private TransN As Single
  49. Private n1 As Single   ' Index of refraction outside the object.
  50. Private n2 As Single   ' Index of refraction inside the object.
  51. Private TransmittedKr As Single
  52. Private TransmittedKg As Single
  53. Private TransmittedKb As Single
  54.  
  55. Private IsReflective As Boolean
  56. Private IsTransparent As Boolean
  57. Private DoneOnThisScanline As Boolean
  58.  
  59. ' We had a hit on this scanline.
  60. Private HadHit As Boolean
  61.  
  62. ' We have had a hit on a previous scanline.
  63. Private HadHitOnPreviousScanline As Boolean
  64.  
  65. ' We will not be visible on later scanlines.
  66. Private ForeverCulled As Boolean
  67. ' Return the right shade for this polygon.
  68. Private Function GetShade(ByVal pgon As SimplePolygon) As Long
  69. Dim i As Integer
  70. Dim px As Single
  71. Dim py As Single
  72. Dim pz As Single
  73. Dim light_source As LightSource
  74. Dim total_r As Single
  75. Dim total_g As Single
  76. Dim total_b As Single
  77. Dim R1 As Integer
  78. Dim g1 As Integer
  79. Dim b1 As Integer
  80. Dim empty_objects As Collection
  81.  
  82.     With pgon
  83.         ' Find a central point on this polygon.
  84.         For i = 1 To .PointX.Count
  85.             px = px + .PointX(i)
  86.             py = py + .PointY(i)
  87.             pz = pz + .PointZ(i)
  88.         Next i
  89.         px = px / .PointX.Count
  90.         py = py / .PointX.Count
  91.         pz = pz / .PointX.Count
  92.  
  93.         ' Add up the light components.
  94.         Set empty_objects = New Collection
  95.         For Each light_source In LightSources
  96.             CalculateHitColorDSA _
  97.                 1, empty_objects, Nothing, _
  98.                 EyeX, EyeY, EyeZ, _
  99.                 px, py, pz, .Nx, .Ny, .Nz, _
  100.                 DiffuseKr, DiffuseKg, DiffuseKb, AmbientKr, AmbientKg, AmbientKb, _
  101.                 SpecularK, SpecularN, R1, g1, b1
  102.             total_r = total_r + R1
  103.             total_g = total_g + g1
  104.             total_b = total_b + b1
  105.         Next light_source
  106.     End With
  107.  
  108.     If total_r > 255 Then total_r = 255
  109.     If total_g > 255 Then total_g = 255
  110.     If total_b > 255 Then total_b = 255
  111.  
  112.     GetShade = RGB(total_r, total_g, total_b)
  113. End Function
  114. ' Draw a face if it is not a backface.
  115. Private Sub DrawFace(ByVal pic As PictureBox, X() As Single, Y() As Single, Z() As Single)
  116. Dim pgon As SimplePolygon
  117. Dim i As Integer
  118.  
  119.     ' Make a polygon.
  120.     Set pgon = New SimplePolygon
  121.     For i = 1 To 4
  122.         pgon.AddPoint X(i), Y(i), Z(i)
  123.     Next i
  124.     pgon.Finish
  125.  
  126.     ' If it is not a backface, draw it.
  127.     If Not pgon.IsBackface() Then
  128.         pgon.ForeColor = GetColor()
  129.         pgon.DrawPolygon pic
  130.     End If
  131. End Sub
  132. ' Add non-backface polygons to this collection.
  133. Public Sub RayTraceable_GetPolygons(ByRef num_polygons As Integer, polygons() As SimplePolygon, ByVal shaded As Boolean)
  134. Dim t As Integer
  135. Dim P As Integer
  136. Dim last_t As Integer
  137. Dim pgon As SimplePolygon
  138. Dim i As Integer
  139. Dim color As Long
  140.  
  141.     ' If all polygons are the same color,
  142.     ' get an appropriate color.
  143.     If Not shaded Then
  144.         color = GetColor()
  145.     End If
  146.  
  147.     last_t = NUM_THETA
  148.     For t = 1 To NUM_THETA
  149.         For P = 1 To NUM_PHI - 1
  150.             ' Make a polygon.
  151.             Set pgon = New SimplePolygon
  152.             With WireFrame(last_t, P)
  153.                 pgon.AddPoint .Trans(1), .Trans(2), .Trans(3)
  154.             End With
  155.             With WireFrame(t, P)
  156.                 pgon.AddPoint .Trans(1), .Trans(2), .Trans(3)
  157.             End With
  158.             With WireFrame(t, P + 1)
  159.                 pgon.AddPoint .Trans(1), .Trans(2), .Trans(3)
  160.             End With
  161.             With WireFrame(last_t, P + 1)
  162.                 pgon.AddPoint .Trans(1), .Trans(2), .Trans(3)
  163.             End With
  164.             pgon.Finish
  165.  
  166.             ' See if this is a backface.
  167.             If Not pgon.IsBackface() Then
  168.                 ' This is not a backface. Add it to
  169.                 ' the list.
  170.                 With pgon
  171.                     ' See if we are shaded.
  172.                     If shaded Then
  173.                         ' We are shaded. Get the
  174.                         ' right color.
  175.                         .ForeColor = GetShade(pgon)
  176.                         .FillColor = .ForeColor
  177.                     Else
  178.                         ' We are not shaded. Use the
  179.                         ' normal colors.
  180.                         .ForeColor = vbBlack
  181.                         .FillColor = color
  182.                     End If
  183.                     num_polygons = num_polygons + 1
  184.                     ReDim Preserve polygons(1 To num_polygons)
  185.                     Set polygons(num_polygons) = pgon
  186.                 End With
  187.             End If
  188.         Next P
  189.         last_t = t
  190.     Next t
  191. End Sub
  192. ' Make a wire frame.
  193. Private Sub MakeWireFrame()
  194. Const PI = 3.14159265
  195.  
  196. Dim i_theta As Integer
  197. Dim i_phi As Integer
  198. Dim theta As Single
  199. Dim phi As Single
  200. Dim dtheta As Single
  201. Dim dphi As Single
  202. Dim X As Single
  203. Dim Y As Single
  204. Dim Z As Single
  205. Dim rad As Single
  206.  
  207.     dtheta = 2 * PI / NUM_THETA
  208.     dphi = PI / (NUM_PHI - 1)
  209.     theta = 0
  210.     For i_theta = 1 To NUM_THETA
  211.         phi = -PI / 2
  212.         For i_phi = 1 To NUM_PHI
  213.             Z = Center.Coord(3) + Radius * Sin(phi)
  214.             rad = Radius * Cos(phi)
  215.             X = Center.Coord(1) + rad * Cos(theta)
  216.             Y = Center.Coord(2) + rad * Sin(theta)
  217.  
  218.             WireFrame(i_theta, i_phi).Coord(1) = X
  219.             WireFrame(i_theta, i_phi).Coord(2) = Y
  220.             WireFrame(i_theta, i_phi).Coord(3) = Z
  221.             WireFrame(i_theta, i_phi).Coord(4) = 1
  222.  
  223.             phi = phi + dphi
  224.         Next i_phi
  225.         theta = theta + dtheta
  226.     Next i_theta
  227. End Sub
  228.  
  229. ' Return an appropriate color for this object.
  230. Private Function GetColor() As Long
  231. Dim R As Integer
  232. Dim G As Integer
  233. Dim B As Integer
  234.  
  235.     R = 255 * (DiffuseKr + AmbientKr): If R > 255 Then R = 255
  236.     G = 255 * (DiffuseKg + AmbientKg): If G > 255 Then G = 255
  237.     B = 255 * (DiffuseKb + AmbientKb): If B > 255 Then B = 255
  238.     GetColor = RGB(R, G, B)
  239. End Function
  240.  
  241. ' Initialize the object using text parameters in
  242. ' a comma-delimited list.
  243. Public Sub SetParameters(ByVal txt As String)
  244.     On Error GoTo SphereParamError
  245.  
  246.     ' Read the parameters and initialize the object.
  247.     ' Geometry.
  248.     Radius = CSng(GetDelimitedToken(txt, ","))
  249.     Center.Coord(1) = CSng(GetDelimitedToken(txt, ","))
  250.     Center.Coord(2) = CSng(GetDelimitedToken(txt, ","))
  251.     Center.Coord(3) = CSng(GetDelimitedToken(txt, ","))
  252.     Center.Coord(4) = 1
  253.  
  254.     ' Ambient light.
  255.     AmbientKr = CSng(GetDelimitedToken(txt, ","))
  256.     AmbientKg = CSng(GetDelimitedToken(txt, ","))
  257.     AmbientKb = CSng(GetDelimitedToken(txt, ","))
  258.  
  259.     ' Diffuse reflection.
  260.     DiffuseKr = CSng(GetDelimitedToken(txt, ","))
  261.     DiffuseKg = CSng(GetDelimitedToken(txt, ","))
  262.     DiffuseKb = CSng(GetDelimitedToken(txt, ","))
  263.  
  264.     ' Specular reflection.
  265.     SpecularN = CSng(GetDelimitedToken(txt, ","))
  266.     SpecularK = CSng(GetDelimitedToken(txt, ","))
  267.  
  268.     ' Reflected light.
  269.     ReflectedKr = CSng(GetDelimitedToken(txt, ","))
  270.     ReflectedKg = CSng(GetDelimitedToken(txt, ","))
  271.     ReflectedKb = CSng(GetDelimitedToken(txt, ","))
  272.     IsReflective = (ReflectedKr > 0) Or (ReflectedKg > 0) Or (ReflectedKb > 0)
  273.  
  274.     ' Transmitted light.
  275.     TransN = CSng(GetDelimitedToken(txt, ","))
  276.     n1 = CSng(GetDelimitedToken(txt, ","))
  277.     n2 = CSng(GetDelimitedToken(txt, ","))
  278.     TransmittedKr = CSng(GetDelimitedToken(txt, ","))
  279.     TransmittedKg = CSng(GetDelimitedToken(txt, ","))
  280.     TransmittedKb = CSng(GetDelimitedToken(txt, ","))
  281.     IsTransparent = (TransmittedKr > 0) Or (TransmittedKg > 0) Or (TransmittedKb > 0)
  282.  
  283.     ' Make a wire frame.
  284.     MakeWireFrame
  285.  
  286.     Exit Sub
  287.  
  288. SphereParamError:
  289.     MsgBox "Error initializing sphere parameters."
  290. End Sub
  291.  
  292. ' Draw a wireframe for this object.
  293. Public Sub RayTraceable_DrawWireFrame(ByVal pic As PictureBox)
  294. Dim t As Integer
  295. Dim P As Integer
  296. Dim last_t As Integer
  297.  
  298.     ' Use an appropriate color.
  299.     pic.ForeColor = GetColor()
  300.  
  301.     last_t = NUM_THETA
  302.     For t = 1 To NUM_THETA
  303.         For P = 1 To NUM_PHI
  304.             With WireFrame(last_t, P)
  305.                 pic.CurrentX = .Trans(1)
  306.                 pic.CurrentY = .Trans(2)
  307.             End With
  308.             With WireFrame(t, P)
  309.                 pic.Line -(.Trans(1), .Trans(2))
  310.             End With
  311.             If P < NUM_PHI Then
  312.                 With WireFrame(t, P + 1)
  313.                     pic.Line -(.Trans(1), .Trans(2))
  314.                 End With
  315.             End If
  316.         Next P
  317.         last_t = t
  318.     Next t
  319. End Sub
  320. ' Draw the object with backfaces removed.
  321. Public Sub RayTraceable_DrawBackfacesRemoved(ByVal pic As PictureBox)
  322. Dim t As Integer
  323. Dim P As Integer
  324. Dim last_t As Integer
  325. Dim X(1 To 4) As Single
  326. Dim Y(1 To 4) As Single
  327. Dim Z(1 To 4) As Single
  328.  
  329.     ' Use an appropriate color.
  330.     pic.ForeColor = GetColor()
  331.  
  332.     last_t = NUM_THETA
  333.     For t = 1 To NUM_THETA
  334.         For P = 1 To NUM_PHI - 1
  335.             With WireFrame(last_t, P)
  336.                 X(1) = .Trans(1)
  337.                 Y(1) = .Trans(2)
  338.                 Z(1) = .Trans(3)
  339.             End With
  340.             With WireFrame(t, P)
  341.                 X(2) = .Trans(1)
  342.                 Y(2) = .Trans(2)
  343.                 Z(2) = .Trans(3)
  344.             End With
  345.             With WireFrame(t, P + 1)
  346.                 X(3) = .Trans(1)
  347.                 Y(3) = .Trans(2)
  348.                 Z(3) = .Trans(3)
  349.             End With
  350.             With WireFrame(last_t, P + 1)
  351.                 X(4) = .Trans(1)
  352.                 Y(4) = .Trans(2)
  353.                 Z(4) = .Trans(3)
  354.             End With
  355.  
  356.             DrawFace pic, X, Y, Z
  357.         Next P
  358.         last_t = t
  359.     Next t
  360. End Sub
  361. ' Apply a transformation matrix to the object.
  362. Public Sub RayTraceable_Apply(M() As Single)
  363. Dim i_theta As Integer
  364. Dim i_phi As Integer
  365.  
  366.     ' Transform the wire frame.
  367.     For i_theta = 1 To NUM_THETA
  368.         For i_phi = 1 To NUM_PHI
  369.             m3Apply WireFrame(i_theta, i_phi).Coord, _
  370.                  M, WireFrame(i_theta, i_phi).Trans
  371.         Next i_phi
  372.     Next i_theta
  373.  
  374.     ' Transform the center.
  375.     m3Apply Center.Coord, M, Center.Trans
  376. End Sub
  377. ' Apply a transformation matrix to the object.
  378. Public Sub RayTraceable_ApplyFull(M() As Single)
  379. Dim i_theta As Integer
  380. Dim i_phi As Integer
  381.  
  382.     ' Transform the wire frame.
  383.     For i_theta = 1 To NUM_THETA
  384.         For i_phi = 1 To NUM_PHI
  385.             m3ApplyFull WireFrame(i_theta, i_phi).Coord, _
  386.                      M, WireFrame(i_theta, i_phi).Trans
  387.         Next i_phi
  388.     Next i_theta
  389.  
  390.     ' Transform the center.
  391.     m3ApplyFull Center.Coord, M, Center.Trans
  392. End Sub
  393.  
  394. ' Return the red, green, and blue components of
  395. ' the surface at the hit position.
  396. Public Sub RayTraceable_FindHitColor( _
  397.     ByVal depth As Integer, Objects As Collection, _
  398.     ByVal eye_x As Single, ByVal eye_y As Single, ByVal eye_z As Single, _
  399.     ByVal px As Single, ByVal py As Single, ByVal pz As Single, _
  400.     ByRef R As Integer, ByRef G As Integer, ByRef B As Integer)
  401. Dim Nx As Single
  402. Dim Ny As Single
  403. Dim Nz As Single
  404. Dim n_len  As Single
  405.  
  406.     ' Find the unit normal at this point.
  407.     Nx = px - Center.Trans(1)
  408.     Ny = py - Center.Trans(2)
  409.     Nz = pz - Center.Trans(3)
  410.     n_len = Sqr(Nx * Nx + Ny * Ny + Nz * Nz)
  411.     Nx = Nx / n_len
  412.     Ny = Ny / n_len
  413.     Nz = Nz / n_len
  414.  
  415.     ' Get the hit color.
  416.     CalculateHitColor depth, Objects, Me, _
  417.         eye_x, eye_y, eye_z, _
  418.         px, py, pz, _
  419.         Nx, Ny, Nz, _
  420.         DiffuseKr, DiffuseKg, DiffuseKb, _
  421.         AmbientKr, AmbientKg, AmbientKb, _
  422.         SpecularK, SpecularN, _
  423.         ReflectedKr, ReflectedKg, ReflectedKb, IsReflective, _
  424.         TransmittedKr, TransmittedKg, TransmittedKb, TransN, n1, n2, IsTransparent, _
  425.         R, G, B
  426. End Sub
  427. ' See if the scanline plane with the indicated
  428. ' point and normal intersects this object. Set
  429. ' the object's DoneOnThisScanline flag appropriately.
  430. Public Sub RayTraceable_CullScanline(ByVal px As Single, ByVal py As Single, ByVal pz As Single, ByVal Nx As Single, ByVal Ny As Single, ByVal Nz As Single)
  431. Dim dx As Single
  432. Dim dy As Single
  433. Dim dz As Single
  434. Dim dist As Single
  435.  
  436.     ' See if we will ever be visible again.
  437.     If ForeverCulled Then
  438.         DoneOnThisScanline = True
  439.         Exit Sub
  440.     End If
  441.  
  442.     ' We have not yet had a hit on this scanline.
  443.     HadHit = False
  444.  
  445.     ' Find the distance from the center of the
  446.     ' sphere to the scanline plane.
  447.  
  448.     ' Get the vector from our center to the point.
  449.     With Center
  450.         dx = .Trans(1) - px
  451.         dy = .Trans(2) - py
  452.         dz = .Trans(3) - pz
  453.     End With
  454.  
  455.     ' Take the dot product of this and the normal.
  456.     ' If the resulting distance > Radius, cull.
  457.     DoneOnThisScanline = (Abs(dx * Nx + dy * Ny + dz * Nz) > Radius)
  458.  
  459.     ' See if we will be culled in the future.
  460.     If DoneOnThisScanline Then
  461.         ' We were not culled on a previous scanline
  462.         ' but we are now. We will be culled on
  463.         ' all later scanlines.
  464.         If HadHitOnPreviousScanline Then ForeverCulled = True
  465.     Else
  466.         ' We are not culled. Remember that.
  467.         HadHitOnPreviousScanline = True
  468.     End If
  469. End Sub
  470. ' Return the value T for the point of intersection
  471. ' between the vector from point (px, py, pz) in
  472. ' the direction <vx, vy, vz>.
  473. '
  474. ' direct_calculation is true if we are finding the
  475. ' intersection from a viewing position ray. It is
  476. ' false if we are finding an reflected intersection
  477. ' or a shadow feeler.
  478. Public Function RayTraceable_FindT(ByVal direct_calculation As Boolean, ByVal px As Single, ByVal py As Single, ByVal pz As Single, ByVal Vx As Single, ByVal Vy As Single, ByVal Vz As Single) As Single
  479. Dim A As Single
  480. Dim B As Single
  481. Dim C As Single
  482. Dim Cx As Single
  483. Dim Cy As Single
  484. Dim Cz As Single
  485. Dim B24AC As Single
  486. Dim t1 As Single
  487. Dim t2 As Single
  488. Dim dx As Single
  489. Dim dy As Single
  490. Dim dz As Single
  491.  
  492.     ' See if we have been culled.
  493.     If direct_calculation And DoneOnThisScanline Then
  494.         RayTraceable_FindT = -1
  495.         Exit Function
  496.     End If
  497.  
  498.     Cx = Center.Trans(1)
  499.     Cy = Center.Trans(2)
  500.     Cz = Center.Trans(3)
  501.  
  502.     ' Get the coefficients for the quadratic.
  503.     A = Vx * Vx + Vy * Vy + Vz * Vz
  504.     B = 2 * Vx * (px - Cx) + _
  505.         2 * Vy * (py - Cy) + _
  506.         2 * Vz * (pz - Cz)
  507.     C = Cx * Cx + Cy * Cy + Cz * Cz + _
  508.         px * px + py * py + pz * pz - _
  509.         2 * (Cx * px + Cy * py + Cz * pz) - _
  510.         Radius * Radius
  511.  
  512.     ' Solve the quadratic A*t^2 + B*t + C = 0.
  513.     B24AC = B * B - 4 * A * C
  514.     If B24AC < 0 Then
  515.         ' There is no real intersection.
  516.         RayTraceable_FindT = -1
  517.  
  518.         ' If we had a hit before on this scanline
  519.         ' but we don't have one now. We are done
  520.         ' for this scanline.
  521.         If HadHit And direct_calculation Then DoneOnThisScanline = True
  522.  
  523.         Exit Function
  524.     ElseIf B24AC = 0 Then
  525.         ' There is one intersection.
  526.         t1 = -B / 2 / A
  527.     Else
  528.         ' There are two intersections.
  529.         B24AC = Sqr(B24AC)
  530.         t1 = (-B + B24AC) / 2 / A
  531.         t2 = (-B - B24AC) / 2 / A
  532.         ' Use only positive t values.
  533.         If t1 < 0.01 Then t1 = t2
  534.         If t2 < 0.01 Then t2 = t1
  535.         ' Use the smaller t value.
  536.         If t1 > t2 Then t1 = t2
  537.     End If
  538.  
  539.     ' If there is no positive t value, there's no
  540.     ' intersection in this direction.
  541.     If t1 < 0.01 Then
  542.         RayTraceable_FindT = -1
  543.  
  544.         ' If we had a hit before on this scanline
  545.         ' but we don't have one now. We are done
  546.         ' for this scanline.
  547.         If HadHit And direct_calculation Then DoneOnThisScanline = True
  548.  
  549.         Exit Function
  550.     End If
  551.  
  552.     ' We had a hit.
  553.     If direct_calculation Then HadHit = True
  554.  
  555.     RayTraceable_FindT = t1
  556. End Function
  557.  
  558. ' Return the minimum and maximum distances from
  559. ' this point.
  560. Private Sub RayTraceable_GetRminRmax(new_min As Single, new_max As Single, ByVal X As Single, ByVal Y As Single, ByVal Z As Single)
  561. Dim dx As Single
  562. Dim dy As Single
  563. Dim dz As Single
  564. Dim dist As Single
  565.  
  566.     dx = X - Center.Trans(1)
  567.     dy = Y - Center.Trans(2)
  568.     dz = Z - Center.Trans(3)
  569.     dist = Sqr(dx * dx + dy * dy + dz * dz)
  570.     new_max = dist + Radius
  571.     new_min = dist - Radius
  572.     If new_min < 0 Then new_min = 0
  573. End Sub
  574. ' Reset the ForeverCulled flag.
  575. Private Sub RayTraceable_ResetCulling()
  576.     ForeverCulled = False
  577.     HadHitOnPreviousScanline = False
  578. End Sub
  579.  
  580.  
  581.